package org.zachary.imitation.raft.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zachary.imitation.raft.core.Replicator;

import java.util.concurrent.locks.ReentrantLock;


/**
 * {@link ThreadId} 对象持有 {@link Replicator} 复制器对象的引用（{@link #data}），即，ThreadId 是对复制器对象的包装，提供了同步锁的功能
 */
public class ThreadId {
	private static final Logger logger = LoggerFactory.getLogger(ThreadId.class);

	public interface OnError {
		void onError(final ThreadId id, final Object data, final int errorCode);
	}

	/** ThreadId 所持有的复制器对象的引用 */
	private final Object data;
	private final ReentrantLock lock = new ReentrantLock();
	private final OnError onError;
	private volatile boolean destroyed;



	public ThreadId(final Object data, final OnError onError) {
		super();
		this.data = data;
		this.onError = onError;
		this.destroyed = false;
	}

	public Object getData() {
		return this.data;
	}

	public Object lock() {
		if (this.destroyed) {
			return null;
		}
		this.lock.lock();
		if (this.destroyed) {
			this.lock.unlock();
			return null;
		}
		return this.data;
	}

	public void unlock() {
		if (!this.lock.isHeldByCurrentThread()) {
			logger.warn("Fail to unlock with {}, the lock is not held by current thread {}.", this.data, Thread.currentThread());
			return;
		}
		this.lock.unlock();
	}

	//让当前线程让出执行行权
	public void join() {
		while (!this.destroyed) {
			// ThreadHelper.onSpinWait();
		}
	}

	@Override
	public String toString() {
		return this.data.toString();
	}

	public void unlockAndDestroy() {
		if (this.destroyed) {
			return;
		}
		this.destroyed = true;
		unlock();
	}

	/** 该方法中会执行 <code>this.onError.onError(this, this.data, errorCode)</code> 这行代码，在这行代码中会继续发送下一个心跳消息 */
	public void setError(final int errorCode) {
		if (this.destroyed) {
			logger.warn("ThreadId: {} already destroyed, ignore error code: {}", this.data, errorCode);
			return;
		}
		this.lock.lock();
		try {
			if (this.destroyed) {
				logger.warn("ThreadId: {} already destroyed, ignore error code: {}", this.data, errorCode);
				return;
			}
			if (this.onError != null) {
				this.onError.onError(this, this.data, errorCode);
			}
		} finally {
			if (!this.destroyed) {
				this.lock.unlock();
			}
		}
	}
}
